package com.kzj.fun.container.root;

import com.kzj.fun.container.ImageBox;
import com.kzj.fun.container.MagnifyingGlass;
import com.kzj.fun.enums.PaintType;
import com.kzj.fun.graph.SpecialRect;
import com.kzj.fun.interfaces.MenuBarFunction;
import com.kzj.fun.interfaces.MenuFunction;
import com.kzj.fun.interfaces.impl.MenuBarFunctionImpl;
import com.kzj.fun.interfaces.impl.MouseListenerPack;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.util.*;
import java.util.List;

public class RootPanel extends JPanel implements MenuFunction {
    public Stack<BufferedImage> imageHistory;
    public Point startPoint,endPoint;
    public SpecialRect rect;
    public JMenuBar menuBar;
    public List<ImageBox> imageBoxList=new LinkedList<>();
    public boolean release=false;
    public Point tempPoint,tempStartPoint,tempEndPoint;
    public MagnifyingGlass glass;
    public RootFrame father;
    public PaintType paintType=PaintType.NULL;
    public int stroke=3;
    public Color color=Color.WHITE;

    public PaintType getPaintType(){
        return this.paintType;
    }
    public RootPanel(BufferedImage backImage,RootFrame father){
        imageHistory=new Stack<>();
        imageHistory.push(backImage);
        this.father=father;
        MouseListenerPack pack=new MouseListenerPack(this);
        this.addMouseListener(pack);
        this.addMouseMotionListener(pack);
        this.setLayout(null);
        this.setVisible(true);
    }
    public boolean remove(ImageBox box){
        Iterator<ImageBox> iterator = imageBoxList.iterator();
        while(iterator.hasNext()){
            if(box==iterator.next()){
                iterator.remove();
                return true;
            }
        }
        return false;
    }
    public void iconified(){
        for (ImageBox imageBox : imageBoxList) {
            imageBox.setVisible(true);
        }
        if(this.menuBar!=null){
            menuBar.setVisible(false);
        }
    }
    public void normal(BufferedImage bufferedImage){
        this.paintType=PaintType.NULL;
        this.imageHistory.clear();
        this.startPoint=null;
        this.endPoint=null;
        this.rect=null;
        this.release=false;
        this.tempStartPoint=null;
        this.tempEndPoint=null;
        this.imageHistory.push(bufferedImage);
        for (ImageBox imageBox : imageBoxList) {
            imageBox.setVisible(false);
        }
    }
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        BufferedImage backImage = imageHistory.peek();
        g.drawImage(backImage,0,0,this.getWidth(),this.getHeight(),this);
        if(startPoint!=null&&endPoint!=null){
            rect=new SpecialRect(startPoint,endPoint);
            rect.draw(g);
        }
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        cancelSelectBox();
        if(getCursor().getType()==Cursor.CROSSHAIR_CURSOR)
            this.release=false;

    }

    @Override
    public void mousePressed(MouseEvent e) {
        if(menuBar!=null)
            menuBar.setVisible(false);
        if(getCursor().getType()==Cursor.CROSSHAIR_CURSOR){
            this.startPoint=e.getPoint();
            this.release=false;
        }
        else tempPoint=e.getPoint();
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        reCalculateMenuBar();
        repaint();
        this.release=true;
        if(startPoint!=null&&endPoint!=null){
            int x=Math.min(startPoint.x,endPoint.x);
            int y=Math.min(startPoint.y,endPoint.y);
            int width=Math.abs(startPoint.x-endPoint.x);
            int height=Math.abs(startPoint.y-endPoint.y);
            tempStartPoint=new Point(x,y);
            tempEndPoint=new Point(x+width,y+height);
        }
    }

    @Override
    public void mouseDragged(MouseEvent e) {
        openMagnifyingGlass(e.getPoint());
        this.repaint();
        switch (getCursor().getType()){
            case Cursor.CROSSHAIR_CURSOR:{
                this.release=false;
                this.endPoint=e.getPoint();
            }break;
            case Cursor.MOVE_CURSOR:{
                int xdif=e.getX()-tempPoint.x;
                int ydif=e.getY()-tempPoint.y;
                startPoint.setLocation(tempStartPoint.x+xdif,tempStartPoint.y+ydif);
                endPoint.setLocation(tempEndPoint.x+xdif,tempEndPoint.y+ydif);
            }break;
            case Cursor.N_RESIZE_CURSOR:{
                int ydif=e.getY()-tempPoint.y;
                startPoint.setLocation(tempStartPoint.x,tempStartPoint.y+ydif);
            }break;
            case Cursor.S_RESIZE_CURSOR:{
                int ydif=e.getY()-tempPoint.y;
                endPoint.setLocation(tempEndPoint.x,tempEndPoint.y+ydif);
            }break;
            case Cursor.W_RESIZE_CURSOR:{
                int xdif=e.getX()-tempPoint.x;
                startPoint.setLocation(tempStartPoint.x+xdif,tempStartPoint.y);
            }break;
            case Cursor.E_RESIZE_CURSOR:{
                int xdif=e.getX()-tempPoint.x;
                endPoint.setLocation(tempEndPoint.x+xdif,tempEndPoint.y);
            }break;
            case Cursor.NW_RESIZE_CURSOR:{
                int xdif=e.getX()-tempPoint.x;
                int ydif=e.getY()-tempPoint.y;
                startPoint.setLocation(tempStartPoint.x+xdif,tempStartPoint.y+ydif);
            }break;
            case Cursor.NE_RESIZE_CURSOR:{
                int xdif=e.getX()-tempPoint.x;
                int ydif=e.getY()-tempPoint.y;
                startPoint.setLocation(tempStartPoint.x,tempStartPoint.y+ydif);
                endPoint.setLocation(tempEndPoint.x+xdif,tempEndPoint.y);
            }break;
            case Cursor.SW_RESIZE_CURSOR:{
                int xdif=e.getX()-tempPoint.x;
                int ydif=e.getY()-tempPoint.y;
                startPoint.setLocation(tempStartPoint.x+xdif,tempStartPoint.y);
                endPoint.setLocation(tempEndPoint.x,tempEndPoint.y+ydif);
            }break;
            case Cursor.SE_RESIZE_CURSOR:{
                int xdif=e.getX()-tempPoint.x;
                int ydif=e.getY()-tempPoint.y;
                endPoint.setLocation(tempEndPoint.x+xdif,tempEndPoint.y+ydif);
            }
        }

    }

    @Override
    public void mouseMoved(MouseEvent e) {
        int cursorSize=32;
        openMagnifyingGlass(e.getPoint());
        if(release&&rect!=null){
            Point point = e.getPoint();
            if(point.x<=rect.getX()-cursorSize/2||point.x>=rect.getX()+rect.getWidth()+cursorSize/2||point.y<=rect.getY()-cursorSize/2||point.y>=rect.getY()+rect.getHeight()+cursorSize/2){
                this.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
            }
            else if(point.x>=rect.getX()+cursorSize/2&&point.x<=rect.getX()+rect.getWidth()-cursorSize/2&&point.y>=rect.getY()+cursorSize/2&&point.y<=rect.getY()+rect.getHeight()-cursorSize/2){
                this.setCursor(new Cursor(Cursor.MOVE_CURSOR));
            }
            else if(point.x>rect.getX()+cursorSize/2&&point.x<rect.getX()+rect.getWidth()-cursorSize/2){
                if(point.y>rect.getY()-cursorSize/2&&point.y<rect.getY()+cursorSize/2){
                    this.setCursor(new Cursor(Cursor.N_RESIZE_CURSOR));
                }else if(point.y>rect.getY()+rect.getHeight()-cursorSize/2&&point.y<rect.getY()+rect.getHeight()+cursorSize/2){
                    this.setCursor(new Cursor(Cursor.S_RESIZE_CURSOR));
                }
            }else if(point.y>rect.getY()+cursorSize/2&&point.y<rect.getY()+rect.getHeight()-cursorSize/2){
                if(point.x>rect.getX()-cursorSize/2&&point.x<rect.getX()+cursorSize/2){
                    this.setCursor(new Cursor(Cursor.W_RESIZE_CURSOR));
                }else if(point.x>rect.getX()+rect.getWidth()-cursorSize/2&&point.x<rect.getX()+rect.getWidth()+cursorSize/2){
                    this.setCursor(new Cursor(Cursor.E_RESIZE_CURSOR));
                }
            }else if(point.y>=rect.getY()-cursorSize/2&&point.y<=rect.getY()+cursorSize/2){
                if(point.x>=rect.getX()-cursorSize/2&&point.x<=rect.getX()+cursorSize/2){
                    this.setCursor(new Cursor(Cursor.NW_RESIZE_CURSOR));
                }else if(point.x>=rect.getX()+rect.getWidth()-cursorSize/2&&point.x<=rect.getX()+rect.getWidth()+cursorSize/2){
                    this.setCursor(new Cursor(Cursor.NE_RESIZE_CURSOR));
                }
            }else if(point.y>=rect.getY()+rect.getHeight()-cursorSize/2&&point.y<=rect.getY()+rect.getHeight()+cursorSize/2){
                if(point.x>=rect.getX()-cursorSize/2&&point.x<=rect.getX()+cursorSize/2){
                    this.setCursor(new Cursor(Cursor.SW_RESIZE_CURSOR));
                }else if(point.x>=rect.getX()+rect.getWidth()-cursorSize/2&&point.x<=rect.getX()+rect.getWidth()+cursorSize/2){
                    this.setCursor(new Cursor(Cursor.SE_RESIZE_CURSOR));
                }
            }else{
                this.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
            }
//            Logger.debug("x: "+point.x+" y: "+point.y+"   -> rect : x:"+rect.getX()+" y:"+rect.getY()+" width:"+rect.getWidth()+" height:"+rect.getHeight());
        }else{
            this.setCursor(new Cursor(Cursor.CROSSHAIR_CURSOR));
        }
    }

    public MenuBarFunction menuFunction(){
       return new MenuBarFunctionImpl(this);
    }
    private void openMagnifyingGlass(Point point){
        if(glass==null){
            glass=new MagnifyingGlass(point,10,10,imageHistory.peek(),this);
            this.add(glass);
        }else{
            glass.update(point,imageHistory.peek());
        }
        repaint();
    }
    public void cancelSelectBox(){
        this.startPoint=null;
        this.endPoint=null;
        if(menuBar!=null)
            menuBar.setVisible(false);
        repaint();
    }
    private void reCalculateMenuBar(){
        if(rect==null)
            return;
        if(menuBar==null){
            menuBar=new RootMenuBar(menuFunction());
            this.add(this.menuBar);
            revalidate();
        }
        int x=rect.getX();
        int y=rect.getY()+rect.getHeight();
        int width=menuBar.getWidth();
        int height=menuBar.getHeight();
        if(x>this.getWidth()-width){
            x=this.getWidth()-width;
        }
        if(y>this.getHeight()-height){
            y=rect.getY()-height;
        }
        menuBar.setBounds(x,y,width,height);
        menuBar.setVisible(true);
    }
}
